#include <iostream>
#include <math.h>

// Vector Template
template <typename T, size_t dimension, bool column>
class Vector{
	private:
	T data[dimension];

	public:
	// Constructor
        Vector();
	Vector(T* data);
	T operator[] (int i) const;
	T& operator[] (int i);
	Vector<T, dimension, column> operator+ (T num);
	Vector<T, dimension, column> operator+ (Vector<T, dimension, column> data);
	Vector<T, dimension, column> operator* (T num);
	Vector<T, dimension, column> operator* (Vector<T, dimension, column> faktor);
	Vector<T, dimension, !(column)>  transpose();
	float length();
	template <typename U, size_t d, bool c>
	friend std::ostream& operator<< (std::ostream& os, const Vector<T, dimension, column>& data);/*{	// Test - can be ignored
		os << "[";	
		for(int i; i<data.getDimension();i++){
			os << data[i];
		}

		os << "]";

		return os;
	}*/
	bool isColumn()const;
	size_t getDimension() const;
};

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column>::Vector(){
	for(int i = 0; i<getDimension();i++){
		data[i] = 0;
	}
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column>::Vector(T* data){
	for(int i = 0; i<getDimension(); i++){
		this->data[i] = *(data+i);
	}
}

template <typename T, size_t dimension, bool column>
T Vector<T, dimension, column>::operator[] (int i) const{
	return this->data[i];
}

template <typename T, size_t dimension, bool column>
T& Vector<T, dimension, column>::operator[] (int i){
	return this->data[i];
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column> Vector<T, dimension, column>::operator+ (T num){
	T tmp[dimension];
	
	for(int i = 0; i<getDimension(); i++){
		tmp[i] = data[i] + num;
	}
	
	Vector<T, dimension, column> newData(tmp);
	
	return newData;
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column> Vector<T, dimension, column>::operator+ (Vector<T, dimension, column> data){
	T tmp[dimension];

	if(isColumn() == data.isColumn()){
		for(int i = 0; i<getDimension();i++){
			tmp[i] = this->data[i] + data[i];
		}	
	}

	Vector<T, dimension, column> newData(tmp);

	return newData;
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column> Vector<T, dimension, column>::operator* (T num){
	T tmp[dimension];
	for (int i = 0; i < getDimension(); i++) {
		tmp[i] = data[i] * num;
	}
	Vector<T, dimension, column> out(tmp);
	return out;
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, column> Vector<T, dimension, column>::operator* (Vector<T, dimension, column> faktor){
	T tmp[dimension];

	if (column != faktor.isColumn()) {
		return 0;
	}
	if (dimension != faktor.getDimension()) {
		return 0;
	}

	for (int i = 0; i < dimension; i++) {
		tmp[i] = data[i] * faktor[i];
	}
	Vector<T, dimension, column> out(tmp);
	return out;
}

template <typename T, size_t dimension, bool column>
Vector<T, dimension, !(column)> Vector<T, dimension, column>::transpose(){
	T newdata[dimension];
	for (int i = 0; i < dimension; i++) {
		newdata[i] = data[i];
	}
	Vector<T, dimension, !(column)> vector(newdata);
	return vector;
}

template <typename T, size_t dimension, bool column>
float Vector<T, dimension, column>::length(){
	T buffer = 0;
	for (int i = 0; i < dimension; i++) {
		buffer += (data[i] * data[i]);
	}
	return sqrt(buffer);
}

template <typename U, size_t d, bool c>
std::ostream& operator<< (std::ostream& os, const Vector<U, d, c>& data){
	os << "[";	
	for(int i = 0; i<data.getDimension();i++){
		os << data[i];
		if(i < data.getDimension()-1){
			os << " ";
		}
	}
	os << "]";	
	
	if(!(data.isColumn())){
		os << "^T";
	}
	
	return os;
}

template <typename T, bool column>
Vector<T, 3, column> crossProduct(Vector<T, 3, column> f, Vector<T, 3, column> g)
{
	if (f.getDimension() != 3 || g.getDimension() != 3) {
		return 0;
	}
	T tmp[3];
	tmp[0] = f[1]*g[2] - f[2]*g[1];
	tmp[1] = f[2]*g[0] - f[0]*g[2];
	tmp[2] = f[0]*g[1] - f[1]*g[0];
	Vector<T, 3, column> out(tmp);
	return out;
}

template <typename T, size_t dimension, bool column>
bool Vector<T, dimension, column>::isColumn()const{
	return column;
}

template <typename T, size_t dimension, bool column>
size_t Vector<T, dimension, column>::getDimension()const{
	return dimension;
}
